package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.constants.BoardStyleData;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.AroundSign;
import com.myk.game.gobangchess.signs.TitleSign;

import java.util.ArrayList;
import java.util.List;

// 即便会写一点注释(给作者自己写的时候看的)，可读性也不会好

/*
 算法有点通用，应该可用于其它数量种无交集落子方式(仅限以上一个落点为基础，确定可落子相对坐标的，每种可落子方式不一定需要可落位置数量相同，其它数量连珠获胜(当然得对局前选定唯一一个数量)。
 若每回合(除了先手第一回合例外)允许走子手数大于等于给出的落子方式，要求每回合走子必须每种落子方式至少选择一次
 若每回合(除了先手第一回合例外)允许走子手数小于等于给出的落子方式，要求每回合走子落子方式不重复，
 欠行负，但连珠同时欠行胜。
 先手第一回合可以改变手数，但第一手任意落所以按改变后的手数-1计算走子方式。
 棋局规则应保证第一回合不能立刻获胜，(但可以自杀)
 注：纯欠行不连珠棋，可以将获胜连珠数改为大于棋盘大小。纯连珠可以把落子方式改为全盘(代码生成-(row-1),-(col+1)到(row-1),(col-1))的大列表，)。
*/

/**
 * 八卦五子棋
 */
public class Knight5GoBangRoom extends Room {

    // A分割线——————————————————————————————————————————————————


    private  final int firstTime = 1;  //先手第一回合手数，可以与其它一样，但计算落子方式需要减1
    private  final int otherTime = 1;  //每回合手数
    private  final int consecutiveNumber = 5;//棋局中连珠达到多少获胜,(需要大于等于2)
    private  final int[][] oneMethod = {{-2, 1}, {-1, 2}, {1, 2}, {2, 1}, {2, -1}, {1, -2}, {-1, -2}, {-2, -1}};  //此处为马步，可以更改，最后所有落子方式不能有交集(没写这个判断)
    //private  final int[][] twoMethod = {{-1, 1}, {-1, 0}, {-1, -1}, {0, 1}, {0, -1}, {1, -1}, {1, 0}, {1, 1}};   //此处为王步

    //更多种落子方式，可以再添加(但只有一种的需要删掉第二个)
    private  final int[][][] allMethod = {oneMethod} ;//按照落子方式数和变量名更改
    private  final String[] colorMethod = {"#6600FF00"};
    private  int[][] allStepColor =new int  [19*19][2];//先填入总行，然后总列

    //从A分割线到这的上面，变量的赋值可直接更改为棋的规则所需

    private  final int allMethodNumber = allMethod.length; //一共几种落子方式(此处为王马共2种)
    private int[] prelocation = null;//上一步落子位置,intest
    private int alreadyMethod = (int)(Math.pow(2,allMethodNumber)-1+0.1);//二进制表示，1表示未被选择为落子方式在当前回合，二进制最后1位表示第0个落子方法,(0.1为保证转换int无误差,下面有几次类似)
    private int flag = 0;// 标记使用变量

    public Knight5GoBangRoom(String roomName) {
        super(roomName, "珍惜泥的五子棋变体，限制落子在上一手的八卦点上", "八卦五子棋", BoardStyleData.getGoBangStyle(15, 15), true, true);
    }

    @Override
    public String getRuleText() {
        return "《八卦棋五子棋》" +
                "黑白双方轮流落子,\n" +
                "除第一手外，每手落子在上一步的马步(八卦点)处\n" +
                "连五或迫使对方无法落子，即获胜。";

    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        allStepColor =new int  [19*19][2];
        prelocation = null;
        alreadyMethod = (int)(Math.pow(2,allMethodNumber)-1+0.1);
        flag = 0;
    }

    @Override
    public void doRule(int row, int col, int value) {
        if (step == 0) {
            curGrids[row][col] = 1;
            prelocation = new int[]{row, col};
            stepAdd();
            signList.clear();
            allStepColor[0] = new int[] {row,col};

            //颜色展开，考虑第一步就欠行
            for (int i = 0; i < allMethodNumber; i++) {
                for (int j = 0; j < allMethod[i].length; j++) {
                    if (0>row+allMethod[i][j][0] || row+allMethod[i][j][0]>=curGrids.length || 0>col+allMethod[i][j][1] || col+allMethod[i][j][1]>=curGrids[0].length){ // 判定在棋盘区域内
                        continue;
                    }
                    List<Integer> ids = new ArrayList<>();
                    ids.add(getIndex(row + allMethod[i][j][0], col + allMethod[i][j][1]));
                    signList.add(new AroundSign(ids, colorMethod[i], colorMethod[i], 1.2f));
                    //范围
                    if (curGrids[row+allMethod[i][j][0]][col+allMethod[i][j][1]]==0 && flag==0){//记录有可落子格子
                        flag = 1;
                    }
                }
            }
            if (flag == 0) { //确定欠行判负
                doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "执白棋，直接欠行战胜" + player_black.getNickNameOrTemp());
                return;
            }
            flag = 0;
            if (firstTime==1) {
                noticeWhiteMove();
            }else {
                noticeBlackMove();
            }
            return;
        } else if (step < firstTime) {

            method(row,col,1,firstTime-step);

            return ;
        } else {
            method(row,col,value,otherTime - ((step - firstTime)%otherTime)) ;
            return;
        }
    }
    private int checkFormation(int row,int col,int value) {//0未连珠，1成功连珠并获胜(Boolean用起来麻烦)
        int rows = curGrids.length-1;
        int cols = curGrids[0].length-1;
        int formationCount = 1;
        //左下到右上
        int left = Math.min(Math.min(rows-row,col),consecutiveNumber-1);
        int right = Math.min(Math.min(row,cols-col),consecutiveNumber-1);
        for (int i = 1; i <= left; i++) {
            if (curGrids[row+i][col-i] == value){
                formationCount++;
            }else{
                break;
            }
        }
        for (int i = 1; i <= right; i++) {
            if (curGrids[row-i][col+i] == value){
                formationCount++;
            }else{
                break;
            }
        }
        if (formationCount >= consecutiveNumber) {
            return 1;
        }else{
            formationCount = 1;
        }
        //水平方向
        left = Math.max(0,col-consecutiveNumber+1); //左侧（竖直时定义为上）
        right = Math.min(col+consecutiveNumber-1,cols); //右侧(竖直时定义为下)
        for (int i = col-1;i>=left;i--) {
            if (curGrids[row][i] == value) {
                formationCount++;
            } else {
                break;
            }
        }
        for (int i = col+1;i<=right;i++){
            if (curGrids[row][i] == value){
                formationCount++;
            }else{
                break;
            }
        }
        if (formationCount >= consecutiveNumber) {
            return 1;
        }else{
            formationCount = 1;
        }
        //左上到右下
        left = Math.min(Math.min(row,col),consecutiveNumber-1);
        right = Math.min(Math.min(rows-row,cols-col),consecutiveNumber-1);
        for (int i = 1;i<=left;i++){
            if (curGrids[row-i][col-i] == value){
                formationCount++;
            }else{
                break;
            }
        }
        for (int i = 1;i<=right;i++){
            if (curGrids[row+i][col+i] == value){
                formationCount++;
            }else{
                break;
            }
        }
        if (formationCount >= consecutiveNumber) {
            return 1;
        }else{
            formationCount = 1;
        }
        //竖直
        left = Math.min(row,consecutiveNumber-1);
        right = Math.min(rows-row,consecutiveNumber-1);
        for (int i = 1;i<=left;i++) {
            if (curGrids[row-i][col] == value){
                formationCount++;
            }else{
                break;
            }
        }
        for (int i = 1;i<=right;i++){
            if (curGrids[row+i][col] == value){
                formationCount++;
            }else{
                break;
            }
        }
        if (formationCount >= consecutiveNumber) {
            return 1;
        }
        return 0;
    }

    private int method(int row,int col,int value,int Time) { //0失败，1成功落子；Time表使用剩余落子回合
        //使用Time大于剩余落子方式数时剩余落子方式时落子任意，Time小于等于剩余落子方式数时落子只可选择未使用的落子方式，(原规则下Time大于等于剩余落子方式的真伪性不会改变，故该方法与原规则等价)
        if (curGrids[row][col] != 0){ //不能在已经走子的地方落子
            return 0;
        }
        if (step ==1 ){
            alreadyMethod = (int)(Math.pow(2,allMethodNumber)-1+0.1);//特判，修bug
        }
        int remain = 0;
        flag = alreadyMethod;//临时计算使用
        while (flag != 0) { //remain统计alreadyMethod的2进制下1数量(未使用的落子方式)
            flag &= flag - 1;
            remain++;
        }
        flag = 0;
        for (int i = 0; i < allMethodNumber; i++) {
            for (int j = 0; j < allMethod[i].length; j++) {
                if (prelocation[0] + allMethod[i][j][0] == row && prelocation[1] +allMethod[i][j][1] == col && curGrids[row][col] == 0){//符合落子方式且目标为空
                    if ((((int)(Math.pow(2,i)+0.1))^alreadyMethod) > alreadyMethod && Time <= remain) { //使用过且Time小于等于未使用落子方式
                        return 0;
                    }
                    signList.clear();
                    curGrids[row][col] = value;
                    stepAdd();
                    prelocation = new int[] {row,col};
                    if (((int)(Math.pow(2,i)+0.1)^alreadyMethod) < alreadyMethod) { //使得alreadyMethod中1变为0
                        alreadyMethod = (int)(Math.pow(2,i)+0.1)^alreadyMethod;
                        remain--;
                    }
                    if (checkFormation(row,col,value) == 1) {
                        int alreadyMethod = (int)(Math.pow(2,allMethodNumber)-1+0.1);
                        if (value == 1) {
                            doOver(WinMode.BLACK_WIN, player_black.getNickNameOrTemp() + "执黑棋，连珠战胜" + player_white.getNickNameOrTemp());
                        } else {
                            doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "执白棋，连珠战胜" + player_black.getNickNameOrTemp());
                        }
                        return 0;
                    }
                    if (((step - firstTime)%otherTime) == 0){
                        alreadyMethod = (int)(Math.pow(2,allMethodNumber)-1+0.1);
                        remain = allMethodNumber;
                        Time = otherTime+1; //加1以使后续可以直接减1，减少判断

                    }

                    for (int m = 0; m < allMethod.length; m++) {
                        for (int n = 0; n < allMethod[m].length; n++) {
                            if (0>row+allMethod[m][n][0] || row+allMethod[m][n][0]>=curGrids.length || 0>col+allMethod[m][n][1] || col+allMethod[m][n][1]>=curGrids[0].length){
                                continue;
                            }
                            if ((((int)(Math.pow(2,m)+0.1))^alreadyMethod) < alreadyMethod || (Time-1) > remain){

                                List<Integer> ids = new ArrayList<>();
                                ids.add(getIndex(row + allMethod[m][n][0], col + allMethod[m][n][1]));
                                signList.add(new AroundSign(ids,  colorMethod[m], colorMethod[m], 1.2f));
                                if (flag==0 && curGrids[row+allMethod[m][n][0]][col+allMethod[m][n][1]]==0 ) {//记录有可落子格子
                                    flag = 1;
                                }
                            }
                        }
                    }
                    if (flag == 0) { //确定欠行判负
                        if (value == 1 ^ (step - firstTime)%otherTime == 0){
                            doOver(WinMode.WHITE_WIN, player_white.getNickNameOrTemp() + "执白棋，欠行战胜" + player_black.getNickNameOrTemp());
                        }else{
                            doOver(WinMode.BLACK_WIN, player_black.getNickNameOrTemp() + "执黑棋，欠行战胜" + player_white.getNickNameOrTemp());
                        }
                        return 0;
                    }
                    flag = 0;

                    allStepColor[step-1] = new int[] {row,col};
                    for (int h=0;h<step;h++) {
                        List<Integer> ids2 = new ArrayList<>();
                        ids2.add(getIndex(allStepColor[h][0], allStepColor[h][1]));
                        int leftText = 1;
                        int rightText = h+1;
                        if (h>=firstTime){ //需要特判，因为java负数取整也是去小数位
                            leftText = (int)((h-firstTime)/otherTime)+2;
                            //rightText = (h - firstTime)%otherTime+1;
                        }
                        //signList.add(new BadgeSign(getIndex(allStepColor[h][0], allStepColor[h][1]),String.valueOf(leftText)+"."+String.valueOf(rightText),4, textColor, ColorCost.TRANSPARENT));
                        //signList.add(new TextSign(getIndex(allStepColor[h][0], allStepColor[h][1]),String.valueOf(leftText)+"."+String.valueOf(rightText), textColor));
                        signList.add(new TitleSign((allStepColor[h][1] + 0.5f) / curGrids[0].length,
                                (allStepColor[h][0] + 0.45f) / curGrids.length,
                                0, ""+leftText, "#FFFF8000", 0.40f));
                    }
                    //String.valueOf(int step/otherTime)+"."
                    if (value == 2 ^ (step - firstTime)%otherTime==0)  {
                        noticeWhiteMove();
                    }else{
                        noticeBlackMove();
                    }
                    return 1;
                }
            }
        }
        return 0; //没有该落子方式
    }

}
